package lastRites.ui;

import java.util.Vector;
import java.util.logging.Logger;

import lastRites.entities.Player;
import lastRites.game.LastRites;
import lastRites.game.LastRitesSettings;

import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.geom.Rectangle;
import org.newdawn.slick.state.StateBasedGame;

public class Inventory {
	private transient int				inventoryMax		= 16, lastX = 25, lastY = 100, invWidth = 230, invHeight = 200;
	private Vector<ItemStack>			itemList			= new Vector<ItemStack>();
	private transient Image				image;
	private transient Player			owner;
	private transient Vector<Rectangle>	itemClickBoxes		= new Vector<Rectangle>();
	private transient Rectangle			inventoryTitleBar	= new Rectangle( lastX, lastY, invWidth, 25 );
	private transient float				invHorScale			= 1.5f, invVertScale = 1.5f;
	public transient boolean			isDraggingInventory	= false;

	public Inventory() {
		this.image = null;
		this.itemClickBoxes.setSize( this.inventoryMax );
		this.itemList.setSize( this.inventoryMax );
		// Create item click boxes
		calibrateClickBoxes();
	}

	public void render(GameContainer container, StateBasedGame game, Graphics g) {
		if( this.isDraggingInventory ) {
			this.lastX = (int)Math.min( container.getInput().getMouseX(), ( LastRites.SCREEN_WIDTH - this.invWidth ) );
			this.lastY = (int)Math.min( container.getInput().getMouseY(), ( LastRites.SCREEN_HEIGHT - this.invHeight ) );
			this.inventoryTitleBar.setX( lastX );
			this.inventoryTitleBar.setY( lastY );
		}
		// Enlarge inventory TODO: enlarge source image?
		// TODO make draggable using these properties
		this.image.draw( lastX, lastY );
		this.owner.getCurrentAnimation().draw( lastX - 36, lastY, 120, 120 );
		float invOffsetX = 56, invOffsetY = 26, horJump = 44, vertJump = 44, rowLength = 4;
		for( int i = 0; i < inventoryMax; i++ ) {
			if( this.itemList.get( i ) == null ) {
				continue;
			}
			float itemDrawX = lastX + invOffsetX + ( i % rowLength ) * horJump, itemDrawY = lastY + invOffsetY + (float)Math.floor( i / rowLength ) * vertJump;
			itemList.get( i ).getItemType().getInventoryImage().draw( itemDrawX, itemDrawY, 32, 32 );
			// Bump numbers if they're two digits
			itemDrawX += itemList.get( i ).getStackSize() >= 10 ? 14 : 23;
			// Write in lower right hand corner
			g.drawString( "" + itemList.get( i ).getStackSize(), itemDrawX, itemDrawY + 16 );
			// Scale back up
		}
		if( LastRites.MOUSE_ITEM != null ) {
			int offsetX = LastRites.MOUSE_ITEM.getItemType().getInventoryImage().getWidth();
			int offsetY = LastRites.MOUSE_ITEM.getItemType().getInventoryImage().getHeight();
			// Adusts icon to be anchor to mouse's tail
			LastRites.MOUSE_ITEM.getItemType().getInventoryImage()
					.draw( container.getInput().getMouseX() + LastRitesSettings.getMouseIconOffsetX(), container.getInput().getMouseY() + LastRitesSettings.getMouseIconOffsetY() );
			g.drawString( "" + LastRites.MOUSE_ITEM.getStackSize(), container.getInput().getMouseX() + LastRitesSettings.getMouseIconOffsetX() + 12,
					container.getInput().getMouseY() + LastRitesSettings.getMouseIconOffsetY() + 8 );
		}
	}

	private void calibrateClickBoxes() {
		float inventoryOffsetX = 54, inventoryOffsetY = 24, horJump = 44, vertJump = 44, rowLength = 4, boxWidth = 36, boxHeight = 36;
		for( int i = 0; i < inventoryMax; i++ ) {
			float itemDrawX = lastX + inventoryOffsetX + ( i % rowLength ) * horJump, itemDrawY = lastY + inventoryOffsetY + (float)Math.floor( i / rowLength ) * vertJump;
			this.itemClickBoxes.set( i, new Rectangle( itemDrawX, itemDrawY, boxWidth, boxHeight ) );
		}
	}

	public boolean removeItem(Item item) {
		for( ItemStack stack : itemList ) {
			if( stack != null && stack.getItemType().equals( item ) ) {
				stack.setStackSize( stack.getStackSize() - 1 );
				cleanInvList();
				return true;
			}
		}
		return false;
	}

	/** Removes THEE actual data object you want to remove.
	 * @param requestedStack A reference to the exact ItemStack object, usually reference from the MOUSE_ITEM
	 * @return if it worked */
	public boolean removeItem(ItemStack exactStack, int amount) {
		for( ItemStack stack : itemList ) {
			if( stack == exactStack && stack.getStackSize() > 0 ) {
				stack.setStackSize( stack.getStackSize() - 1 );
				cleanInvList();
				return true;
			}
		}
		return false;
	}

	/** Removes the first instance of the passed in templated ItemStack it meets. You don't need to pass THEE actual data object you want to
	 * remove just the same type and size and this function will remove the first instance it comes across in the inventory.
	 * @param requestedStack Whatever object is passed here will be cloned and left untouched.
	 * @return how many items it could remove */
	public int removeItem(ItemStack requestedStack) {
		requestedStack = requestedStack.clone();
		int retVal = 0;
		for( ItemStack currStack : itemList ) {
			if( currStack != null && currStack.getItemType().equals( requestedStack.getItemType() ) ) {
				if( currStack.getStackSize() < requestedStack.getStackSize() ) {
					retVal += currStack.getStackSize();
					requestedStack.setStackSize( requestedStack.getStackSize() - currStack.getStackSize() );
					currStack.setStackSize( 0 );
				} else {
					retVal += requestedStack.getStackSize();
					currStack.setStackSize( currStack.getStackSize() - requestedStack.getStackSize() );
					requestedStack.setStackSize( 0 );
				}
				cleanInvList();
				return retVal + ( requestedStack.getStackSize() > 0 ? removeItem( requestedStack ) : 0 );
			}
		}
		cleanInvList();
		return 0;
	}

	/** Will verify whether or not this inventory contains those item stacks. Will NOT work if you pass it separate stacks of the same
	 * ItemType.
	 * @param items Array of ItemStacks
	 * @return True if the inventory contains the same or greater item quantities than those asked (passed in) */
	public boolean hasItems(Vector<ItemStack> items) {
		for( ItemStack neededStack : items ) {
			if( neededStack == null ) {
				continue;
			}
			int foundQuantity = 0;
			for( ItemStack checkStack : this.getInventory() ) {
				if( checkStack != null && checkStack.getItemType().equals( neededStack.getItemType() ) ) {
					foundQuantity += Math.min( checkStack.getStackSize(), neededStack.getStackSize() );
					if( foundQuantity >= neededStack.getStackSize() ) {
						break;
					}
				}
			}
			if( foundQuantity < neededStack.getStackSize() ) { return false; }

		}
		return true;
	}

	/** Will verify whether or not this inventory contains those item stacks. Will NOT work if you pass it separate stacks of the same
	 * ItemType.
	 * @param items Array of ItemStacks
	 * @return True if the inventory contains the same or greater item quantities than those asked (passed in) */
	public boolean hasItems(ItemStack... items) {
		Vector<ItemStack> repack = new Vector<>();
		for( ItemStack s : items ) {
			repack.add( s );
		}
		return hasItems( repack );
	}

	public boolean addItem(ItemStack items) {
		if( items == null ) {
			Logger.getLogger( "Inventory", "Tried to add a NULL ItemStack to Inventory." );
			return false;
		}
		for( ItemStack stack : itemList ) {
			if( stack != null && stack.getItemType().equals( items.getItemType() ) && stack.getStackSize() < stack.getItemType().getMaxStack() ) {
				if( stack.getStackSize() + items.getStackSize() > stack.getItemType().getMaxStack() ) {
					int diff = stack.getItemType().getMaxStack() - stack.getStackSize();
					stack.setStackSize( stack.getStackSize() + diff );
					items.setStackSize( items.getStackSize() - diff );
					return addItem( items );
				} else {
					stack.setStackSize( stack.getStackSize() + items.getStackSize() );
				}
				return true;
			}
		}
		if( this.hasEmptySlot() ) {
			if( items.getStackSize() > items.getItemType().getMaxStack() ) {
				ItemStack newStack = new ItemStack( items.getItemType(), items.getItemType().getMaxStack() );
				items.setStackSize( items.getStackSize() - items.getItemType().getMaxStack() );
				itemList.set( this.getEmptySlot(), newStack );
				return addItem( items );
			}
			itemList.set( this.getEmptySlot(), items );
			return true;
		}
		return false;
	}

	public boolean addItem(Item item) {
		if( item == null ) {
			Logger.getLogger( "Inventory", "Tried to add a NULL Item to Inventory." );
			return false;
		}
		for( ItemStack stack : itemList ) {
			if( stack != null && stack.getItemType().equals( item ) && stack.getStackSize() < stack.getItemType().getMaxStack() ) {
				stack.setStackSize( stack.getStackSize() + 1 );
				return true;
			}
		}
		if( this.hasEmptySlot() ) {
			ItemStack newStack = new ItemStack( item, 1 );
			itemList.set( this.getEmptySlot(), newStack );
			return true;
		}
		return false;
	}

	private boolean hasEmptySlot() {
		for( ItemStack s : this.itemList ) {
			if( s == null ) { return true; }
		}
		return false;
	}

	private int getEmptySlot() {
		int slot = 0;
		for( ItemStack s : this.itemList ) {
			if( s == null ) { return slot; }
			slot++;
		}
		return slot;
	}

	private void cleanInvList() {
		for( int i = 0; i < itemList.size(); i++ ) {
			if( itemList.get( i ) != null && itemList.get( i ).getStackSize() == 0 ) {
				itemList.set( i, null );
			}
		}
	}

	public void checkBoxClicks(float mouseX, float mouseY) {
		// Determine which, if any, box they clicked in
		int boxNum = 0;
		if( this.inventoryTitleBar.contains( mouseX, mouseY ) ) {
			isDraggingInventory = true;
			this.lastX = (int)mouseX;
			this.lastY = (int)mouseY;
			this.inventoryTitleBar.setX( mouseX );
			this.inventoryTitleBar.setY( mouseY );
		} else {
			for( Rectangle r : this.itemClickBoxes ) {
				if( r.contains( mouseX, mouseY ) ) {
					break;
				}
				boxNum++;
			}
			if( boxNum >= this.inventoryMax ) {
				LastRites.MOUSE_ITEM = null;
				return;
			}
			if( LastRites.MOUSE_ITEM == null ) {
				// Add to cursor if they didn't have one picked up
				LastRites.MOUSE_ITEM = this.itemList.get( boxNum );
			} else if( LastRites.MOUSE_ITEM == this.itemList.get( boxNum ) ) {
				// Clear cursor if they reclicked the same item
				LastRites.MOUSE_ITEM = null;
			} else {
				// Swap the items places
				int swapPos = 0;
				for( ItemStack origStack : this.itemList ) {
					if( origStack == LastRites.MOUSE_ITEM ) {
						break;
					}
					swapPos++;
				}
				ItemStack destinationSwap = this.itemList.get( boxNum );
				if( destinationSwap != null && destinationSwap.getItemType().equals( LastRites.MOUSE_ITEM.getItemType() ) && destinationSwap != LastRites.MOUSE_ITEM && destinationSwap.getStackSize() < 16 ) {
					int roomToGrow = 16 - destinationSwap.getStackSize();
					destinationSwap.setStackSize( destinationSwap.getStackSize() + Math.min( LastRites.MOUSE_ITEM.getStackSize(), roomToGrow ) );
					LastRites.MOUSE_ITEM.setStackSize( LastRites.MOUSE_ITEM.getStackSize() - Math.min( LastRites.MOUSE_ITEM.getStackSize(), roomToGrow ) );
					if( LastRites.MOUSE_ITEM.getStackSize() == 0 ) {
						LastRites.MOUSE_ITEM = null;
						cleanInvList();
					}
				} else {
					this.itemList.set( boxNum, LastRites.MOUSE_ITEM );
					LastRites.MOUSE_ITEM = null;
					this.itemList.set( swapPos, destinationSwap );
				}
			}
		}
	}

	public Vector<ItemStack> getInventory() {
		return itemList;
	}

	public void setInventory(Vector<ItemStack> inventory) {
		this.itemList = inventory;
	}

	public int getInventoryMax() {
		return inventoryMax;
	}

	public void setInventoryMax(int inventoryMax) {
		this.inventoryMax = inventoryMax;
	}

	public void clearInventory() {
		this.itemList = new Vector<ItemStack>();
	}

	public Image getImage() {
		return image;
	}

	public void setImage(Image image) {
		this.image = image;
	}

	public int getLastX() {
		return lastX;
	}

	public void setLastX(int lastX) {
		this.lastX = lastX;
	}

	public int getLastY() {
		return lastY;
	}

	public void setLastY(int lastY) {
		this.lastY = lastY;
	}

	public Player getOwner() {
		return owner;
	}

	public void setOwner(Player owner) {
		this.owner = owner;
	}

	public Rectangle getUIBoundingBox() {
		return new Rectangle( lastX, lastY, this.invWidth, this.invHeight );
	}

	public boolean isDraggingInventory() {
		return isDraggingInventory;
	}

	public void setDraggingInventory(boolean isDraggingInventory) {
		this.isDraggingInventory = isDraggingInventory;
		if( !isDraggingInventory ) {
			calibrateClickBoxes();
		}
	}
}
